using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class IntegerProductionSpecification {
        [Test]
        public void ShouldReturnNewIdentifierTermAndIncrementIndexIfIdentifierToken () {
            var id = IntegerProduction.CreateTerminal ();

            var source = "8 b c";

            int index = 0;
            var result = id.Parse (source, index, new ParseContext ());

            Assert.AreEqual (1, result.Index);
            Assert.IsTrue (result.Term is IntegerTerm);
            var integer = (IntegerTerm) result.Term;
            Assert.AreEqual (8, integer.Value);
            Assert.AreEqual (0, integer.SourceInformation.Index);
            Assert.AreEqual (1, integer.SourceInformation.Length);
        }

        private void AssertIntegerParsed (int i) {
            string stringInteger = i.ToString ();

            AssertIntegerParsed (i, stringInteger);
        }

        private void AssertIntegerParsed (int i, string integerString) {
            var source = integerString;

            ParseResult result = IntegerProduction.CreateTerminal ().Parse (source, 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (integerString.Length, result.Index);
            var integerTerm = result.Term as IntegerTerm;
            Assert.IsNotNull (integerTerm);
            Assert.AreEqual (i, integerTerm.Value);
        }

        private void AssertIntegerPartiallyParsed (string sourceString, int expected, string remainder) {
            var source = sourceString;

            ParseResult result = IntegerProduction.CreateTerminal ().Parse (source, 0, new ParseContext ());

            Assert.IsNotNull (result);
            Assert.AreEqual (sourceString.Substring (0, sourceString.Length - remainder.Length).Length, result.Index);
            var integerTerm = result.Term as IntegerTerm;
            Assert.IsNotNull (integerTerm);
            Assert.AreEqual (expected, integerTerm.Value);
        }

        private void AssertIntegerNotParsed (string integerString) {
            var source = integerString;

            ParseResult result = IntegerProduction.CreateTerminal ().Parse (source, 0, new ParseContext ());

            Assert.IsNull (result);
        }

        [Test]
        public void AssertParseValidIntegers () {
            AssertIntegerParsed (1);
            AssertIntegerParsed (123);
            AssertIntegerParsed (987, "0987");
        }

        [Test]
        public void AssertNotParseInvalidIntegers () {
            AssertIntegerNotParsed ("a89");
            AssertIntegerNotParsed ("-9");
            AssertIntegerNotParsed ("");
        }

        [Test]
        public void AssertPartiallyParseValidIntegers () {
            AssertIntegerPartiallyParsed ("9a", 9, "a");
            AssertIntegerPartiallyParsed ("9-", 9, "-");
            AssertIntegerPartiallyParsed ("0{", 0, "{");
        }

        [Test]
        public void ShouldReturnNullAndNotIncrementIndexIfNotIdentifierToken () {
            var id = IntegerProduction.CreateTerminal ();

            var source = "a b c";

            var result = id.Parse (source, 0, null);

            Assert.IsNull (result);
        }

        [Test]
        public void ShouldReturnNullIfIndexIsOutOfTokenArray () {
            var id = IntegerProduction.CreateTerminal ();

            var source = "a b 8";

            var result = id.Parse (source, 5, null);

            Assert.IsNull (result);
        }

        [Test]
        public void NameShouldBeInteger () {
            IProduction p = IntegerProduction.CreateTerminal ();
            Assert.That (p.Name, Is.EqualTo ("integer"));
        }
    }
}